<template>
  <!-- #ifndef APP-NVUE || WEB -->
  <view
    class="lime-echart"
    :style="[lStyle]"
    v-if="canvasId"
    ref="chartContainer"
    :aria-label="'图表'"
  >
    <canvas
      class="lime-echart__canvas"
      type="2d"
      :style="[styles]"
      :id="canvasId"
      :disable-scroll="isDisableScroll"
      :canvas-id="canvasId"
      @touchstart="handleTouchStart"
      @touchmove="handleTouchMove"
      @touchend="handleTouchEnd"
    >
    </canvas>
  </view>
  <!-- #endif -->
  <!-- #ifdef WEB -->
  <div class="lime-echart" ref="chartContainer" :style="[styles, lStyle]"></div>
  <!-- #endif -->
  <!-- #ifdef APP-NVUE -->
  <view class="lime-echart" :style="[lStyle]">
    <web-view
      class="lime-echart__canvas"
      :webview-styles="webviewStyles"
      :style="[styles]"
      ref="chartContainer"
      src="/uni_modules/lime-echart/static/app/uvue.html?v=1"
      @pagefinish="isInitialized = true"
      @onPostMessage="handleWebviewMessage"
    ></web-view>
  </view>
  <!-- #endif -->
</template>

<script lang="ts">
// @ts-nocheck
import {
  defineComponent,
  getCurrentInstance,
  ref,
  onMounted,
  nextTick,
  onBeforeUnmount,
  watch,
  computed,
} from "./vue";
import echartProps from "./props";

// #ifndef APP-NVUE || WEB
import { Canvas, setCanvasCreator, dispatch } from "./canvas";
import {
  wrapTouch,
  convertTouchesToArray,
  devicePixelRatio,
  sleep,
  canIUseCanvas2d,
  getRect,
  getDeviceInfo,
} from "./utils";
// #endif
// #ifdef APP-NVUE
import { base64ToPath, sleep } from "./utils";
import { Echarts } from "./nvue";
// #endif
// #ifdef WEB
import * as echartsLibrary from "@/uni_modules/lime-echart/static/web/echarts.esm.min.js";
// #endif

// #ifdef APP-VUE
import "@/uni_modules/lime-echart/static/app/echarts.min.js";
const echartsLibrary = globalThis.echarts;
// #endif

export default defineComponent({
  props: echartProps,
  emits: ["finished"],
  setup(props, { emit, expose }) {
    // #ifndef APP-NVUE || WEB || APP-VUE
    let echartsLibrary = null;
    // #endif

    const instance = getCurrentInstance()!;
    const canvasId = `lime-echart-${instance.uid}`;
    const isInitialized = ref(false);
    const chartContainer = ref(null);

    type ChartOptions = Record<string, any>;
    type EChartsInstance = typeof echartsLibrary;
    type EChartsResolveCallback = (value: EChartsInstance) => void;

    const initializationQueue = [] as EChartsResolveCallback[];
    const callbackQueue = [] as EChartsResolveCallback[];

    let chartInstance: null | EChartsInstance = null;

    const styles = computed(() => {
      if (props.landscape) {
        return {
          transform: "translate(-50%,-50%) rotate(90deg)",
          top: "50%",
          left: "50%",
        };
      }
      return {};
    });

    const checkInitialization = (): boolean => {
      if (chartInstance) return false;
      console.warn(`组件还未初始化，请先使用 init`);
      return true;
    };

    const setOption = (options: ChartOptions) => {
      if (checkInitialization()) return;
      chartInstance!.setOption(options);
    };

    const hideLoading = () => {
      if (checkInitialization()) return;
      chartInstance!.showLoading();
    };

    const showLoading = () => {
      if (checkInitialization()) return;
      chartInstance!.hideLoading();
    };

    const clear = () => {
      if (checkInitialization()) return;
      chartInstance!.clear();
    };

    const dispose = () => {
      if (checkInitialization()) return;
      chartInstance!.dispose();
    };
    const processInitializationQueue = () => {
      while (initializationQueue.length > 0) {
        if (chartInstance != null) {
          const resolve = initializationQueue.pop() as EChartsResolveCallback;
          resolve(chartInstance!);
        }
      }

      if (chartInstance != null) {
        while (callbackQueue.length > 0) {
          const callback = callbackQueue.pop() as EChartsResolveCallback;
          callback(chartInstance!);
        }
      }
    };

    const resize = (dimensions?: { width?: number; height?: number }) => {
      if (checkInitialization()) return;
      // #ifdef APP-NVUE || WEB
      chartInstance!.resize(dimensions);
      // #endif
      // #ifndef APP-NVUE || WEB
      getRect(`#${canvasId}`, instance.proxy).then((res) => {
        chartInstance!.resize({ width: res.width, height: res.height });
      });
      // #endif
    };

    // #ifdef APP-NVUE
    let chartFile = ref(null);
    const handleWebviewMessage = (e) => {
      const detail = e?.detail?.data[0] || null;
      const data = detail?.data;
      const key = detail?.event;
      const options = data?.options;
      const event = data?.event;
      const file = detail?.file;
      if (key == "log" && data) {
        console.log(data);
      }
      if (event) {
        chartInstance.dispatchAction(event.replace(/"/g, ""), options);
      }
      if (file) {
        chartFile.value = file;
      }
    };

    const canvasToTempFilePath = (options: ChartOptions) => {
      if (checkInitialization()) return;
      chartContainer.value.evalJs(`canvasToTempFilePath()`);
      watch(chartFile, async (file) => {
        if (!file) return;
        const tempFilePath = await base64ToPath(file);
        options.success({ tempFilePath });
      });
    };

    const getContext = () => {
      if (isInitialized.value) {
        return Promise.resolve(isInitialized.value);
      }
      return new Promise((resolve) => {
        watch(isInitialized, (val) => {
          if (!val) return;
          resolve(val);
        });
      });
    };
    const init = async (echarts, ...args) => {
      let theme: string | null = null;
      let config: Record<string, any> = {};
      let callback: Function | null = null;

      args.forEach((item) => {
        if (typeof item === "function") {
          callback = item;
        } else if (typeof item === "string") {
          theme = item;
        } else if (typeof item === "object") {
          config = item;
        }
      });
      if (props.beforeDelay) {
        await sleep(props.beforeDelay);
      }
      await getContext();
      chartInstance = new Echarts(chartContainer.value);
      chartContainer.value.evalJs(
        `init(null, null, ${JSON.stringify(config)}, ${theme})`,
      );
      if (callback && typeof callback === "function") {
        callbackQueue.push(callback);
      }

      return new Promise<EChartsInstance>((resolve) => {
        nextTick(() => {
          initializationQueue.push(resolve);
          processInitializationQueue();
        });
      });
    };

    // #endif

    // #ifndef APP-NVUE || WEB
    let canvasNode;
    const canvasToTempFilePath = (options: ChartOptions) => {
      if (checkInitialization()) return;
      if (canvasNode) {
        options.success?.({
          tempFilePath: canvasNode.toDataURL(),
        });
      } else {
        uni.canvasToTempFilePath(
          {
            ...options,
            canvasId,
          },
          instance.proxy,
        );
      }
    };

    const getContext = () => {
      return getRect(`#${canvasId}`, instance.proxy).then((res) => {
        let dpr = devicePixelRatio;
        let { width, height, node } = res;
        let canvas: Canvas | null = null;
        if (!(width || height)) {
          return Promise.reject("no rect");
        }
        if (node && node.getContext) {
          const ctx = node.getContext("2d");
          canvas = new Canvas(ctx, instance.proxy, true, node);
          canvasNode = node;
        } else {
          dpr = 1;
          const ctx = uni.createCanvasContext(canvasId, instance.proxy);
          canvas = new Canvas(ctx, instance.proxy, false);
        }
        return { canvas, width, height, devicePixelRatio: dpr, node };
      });
    };
    const getTouch = (e) => {
      const touches = e.touches[0];
      const touch = props.landscape
        ? {
            x: touches.y,
            y: touches.x,
          }
        : {
            x: touches.x,
            y: touches.y,
          };
      return touch;
    };
    const handleTouchStart = (e) => {
      if (chartInstance == null) return;
      const handler = chartInstance.getZr().handler;
      const touch = getTouch(e);
      dispatch.call(handler, "mousedown", touch);
      dispatch.call(handler, "click", touch);
    };
    const handleTouchMove = (e) => {
      if (chartInstance == null) return;
      const handler = chartInstance.getZr().handler;
      const touch = getTouch(e);
      dispatch.call(handler, "mousemove", touch);
    };
    const handleTouchEnd = (e) => {
      if (chartInstance == null) return;
      const handler = chartInstance.getZr().handler;

      const touch = {
        x: 999999999,
        y: 999999999,
      };

      dispatch.call(handler, "mousemove", touch);
      dispatch.call(handler, "touchend", touch);
    };
    const init = async (
      echartsLib: EChartsInstance = echartsLibrary,
      ...args: any[]
    ): Promise<EChartsInstance> => {
      const library = echartsLib || echartsLibrary;
      if (!library) {
        console.error("ECharts library is required");
        return Promise.reject("ECharts library is required");
      }
      let theme: string | null = null;
      let config: Record<string, any> = {};
      let callback: Function | null = null;

      args.forEach((item) => {
        if (typeof item === "function") {
          callback = item;
        } else if (typeof item === "string") {
          theme = item;
        } else if (typeof item === "object") {
          config = item;
        }
      });
      if (props.beforeDelay) {
        await sleep(props.beforeDelay);
      }
      let options = await getContext();
      setCanvasCreator(library, options);
      chartInstance = library.init(
        options.canvas,
        theme,
        Object.assign({}, options, config),
      );
      if (callback && typeof callback === "function") {
        callbackQueue.push(callback);
      }
      return new Promise<EChartsInstance>((resolve) => {
        initializationQueue.push(resolve);
        processInitializationQueue();
      });
    };

    // #endif

    // #ifdef WEB
    const canvasToTempFilePath = (options: ChartOptions) => {
      if (checkInitialization()) return;
      options.success?.({
        tempFilePath: chartInstance._api.getDataURL(),
      });
    };

    const init = async (
      echarts: EChartsInstance = echartsLibrary,
      ...args: any[]
    ): Promise<EChartsInstance> => {
      const library = echarts || echartsLibrary;
      if (!library) {
        console.error("ECharts library is required");
        return Promise.reject("ECharts library is required");
      }

      let theme: string | null = null;
      let config = {};
      let callback: Function | null = null;

      args.forEach((item) => {
        if (typeof item === "function") {
          callback = item;
        } else if (typeof item === "string") {
          theme = item;
        } else if (typeof item === "object") {
          config = item;
        }
      });

      // Configure ECharts environment
      library.env.domSupported = true;
      library.env.hasGlobalWindow = true;
      library.env.node = false;
      library.env.pointerEventsSupported = false;
      library.env.svgSupported = true;
      library.env.touchEventsSupported = true;
      library.env.transform3dSupported = true;
      library.env.transformSupported = true;
      library.env.worker = false;
      library.env.wxa = false;

      chartInstance = library.init(chartContainer.value, theme, config);

      if (callback != null && typeof callback === "function") {
        callbackQueue.push(callback);
      }

      return new Promise<EChartsInstance>((resolve) => {
        initializationQueue.push(resolve);
        processInitializationQueue();
      });
    };
    // #endif

    onMounted(() => {
      nextTick(() => {
        // #ifndef APP-NVUE
        isInitialized.value = true;
        // #endif
        emit("finished");
        processInitializationQueue();
      });
    });
    onBeforeUnmount(() => {
      clear();
      dispose();
    });

    // #ifdef VUE3
    expose({
      init,
      setOption,
      hideLoading,
      showLoading,
      clear,
      dispose,
      resize,
      canvasToTempFilePath,
    });
    // #endif

    return {
      canvasId,
      chartContainer,
      styles,
      // #ifndef WEB || APP-NVUE
      handleTouchStart,
      handleTouchMove,
      handleTouchEnd,
      // #endif

      // #ifdef APP-NVUE
      handleWebviewMessage,
      isInitialized,
      // #endif

      // #ifdef VUE2
      init,
      setOption,
      hideLoading,
      showLoading,
      clear,
      dispose,
      resize,
      canvasToTempFilePath,
      // #endif
    };
  },
});
</script>

<style>
.lime-echart {
  position: relative;
  /* #ifndef APP-NVUE */
  width: 100%;
  height: 100%;
  /* #endif */
  /* #ifdef APP-NVUE */
  flex: 1;
  /* #endif */
}

.lime-echart__canvas {
  /* #ifndef APP-NVUE */
  width: 100%;
  height: 100%;
  /* #endif */
  /* #ifdef APP-NVUE */
  flex: 1;
  /* #endif */
}

/* #ifndef APP-NVUE */
.lime-echart__mask {
  position: absolute;
  width: 100%;
  height: 100%;
  left: 0;
  top: 0;
  z-index: 1;
}
/* #endif */
</style>
